package com.spring.sd;

import com.spring.sd.scope2.E1;
import com.spring.sd.scope2.E2;
import com.spring.sd.scope2.E3;
import com.spring.sd.scope2.E4;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/**
 * Scope 失效分析
 * 如果对性能要求较高，则推荐使用后两种方式，前两种使用代理会有一定的性能损耗；如果不在乎那点性能损耗，则可以使用第一种方式，这种方式最简单。
 *
 * 四种解决方式虽然不同，但在理念上殊途同归，都是推迟了其他 Scope Bean 的获取，或者说按需加载。
 *
 */
@Slf4j
@ComponentScan("com.spring.sd.scope2")
public class ScopeApplication2 {

    public static void main(String[] args) {
        //step1();
        //step2();
        //step3();
        //step4();
        step5();
    }

    public static void step1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScopeApplication2.class);

        E1 e1 = context.getBean(E1.class);

        // 获取到的 f1 居然都是同一个，也就是说向单例对象中注入多例对象失败了
        log.info("{}", e1.getF1());
        log.info("{}", e1.getF1());
        log.info("{}", e1.getF1());

        // 对于单例对象e来说，依赖注入仅发生了一次，后续不会再注入其他的 f1，因此 e 始终使用的是第一次注入的 f1
        context.close();
    }

    /**
     * 为了解决这个问题，可以使用 @Lazy 生成代理对象，虽然代理对象依旧是同一个，但每次使用代理对象中的方法时，会由代理对象创建新的目标对象
     *
     */
    public static void step2() {
        // @Lazy
        // @Autowired
        // private F1 f1;
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScopeApplication2.class);

        E1 e1 = context.getBean(E1.class);

        // 获取到的 f1 居然都是同一个，也就是说向单例对象中注入多例对象失败了
        log.info("{}", e1.getF1());
        log.info("{}", e1.getF1());
        log.info("{}", e1.getF1());

        // 对于单例对象e来说，依赖注入仅发生了一次，后续不会再注入其他的 f1，因此 e 始终使用的是第一次注入的 f1
        context.close();
    }

    /**
     * 除了使用 @Lazy 注解外，可以使用 @Scope 注解的 proxyMode 属性指定代理模式：
     */
    public static void step3() {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScopeApplication2.class);

        E2 e = context.getBean(E2.class);

        // 获取到的 f1 居然都是同一个，也就是说向单例对象中注入多例对象失败了
        log.info("{}", e.getF2());
        log.info("{}", e.getF2());
        log.info("{}", e.getF2());

        // 对于单例对象e来说，依赖注入仅发生了一次，后续不会再注入其他的 f1，因此 e 始终使用的是第一次注入的 f1
        context.close();
    }


    public static void step4() {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScopeApplication2.class);

        E3 e = context.getBean(E3.class);

        // 获取到的 f1 居然都是同一个，也就是说向单例对象中注入多例对象失败了
        log.info("{}", e.getF3());
        log.info("{}", e.getF3());
        log.info("{}", e.getF3());

        // 对于单例对象e来说，依赖注入仅发生了一次，后续不会再注入其他的 f1，因此 e 始终使用的是第一次注入的 f1
        context.close();
    }

    public static void step5() {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScopeApplication2.class);

        E4 e = context.getBean(E4.class);

        // 获取到的 f1 居然都是同一个，也就是说向单例对象中注入多例对象失败了
        log.info("{}", e.getF4());
        log.info("{}", e.getF4());
        log.info("{}", e.getF4());

        // 对于单例对象e来说，依赖注入仅发生了一次，后续不会再注入其他的 f1，因此 e 始终使用的是第一次注入的 f1
        context.close();
    }
}
